﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace Vision.Common
{
    /// <summary>
    /// The derived Class SHOULD have three constructors.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class TypedConfigurationSectionEx<T> : TypedConfigurationSection, IInstancable<T>
    {
        #region Const
        const string TargetPropetyName = "target";
        #endregion

        #region Method Info
        static readonly MethodInfo DeserializeElementMethodInfo = typeof(ConfigurationElement).GetMethod("DeserializeElement", BindingFlags.NonPublic | BindingFlags.Instance);
        static readonly MethodInfo SerializeElementMethodInfo = typeof(ConfigurationElement).GetMethod("SerializeElement", BindingFlags.NonPublic | BindingFlags.Instance);
        #endregion

        #region Ctors
        public TypedConfigurationSectionEx()
            : base()
        {
        }

        public TypedConfigurationSectionEx(Type type)
            : base(type)
        {
        }

        public TypedConfigurationSectionEx(T target)
            : this(typeof(T))
        {
            Target = target;
        }
        #endregion

        #region Current Property
        [ConfigurationProperty(TargetPropetyName)]
        public virtual T Target
        {
            get
            {
                return (T)this[TargetPropetyName];
            }
            set
            {
                this[TargetPropetyName] = value;
            }
        }
        #endregion

        #region Override
        protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            if (reader != null)
            {
                if (!string.IsNullOrEmpty(reader.GetAttribute(TypeNamePropertyName)))
                {
                    var typename = reader.GetAttribute(TypeNamePropertyName);
                    var elementType = default(Type);
                    try
                    {
                        elementType = Type.GetType(typename);
                        var allAttributes = elementType.GetCustomAttributes(typeof(ConfigurationElementTypeAttribute), true);
                        if (allAttributes.Length > 0)
                        {
                            var typeAttribute = allAttributes[0] as ConfigurationElementTypeAttribute;
                            var configurationType = typeAttribute.ConfigurationType;
                            if (typeof(TypedConfigurationSection).IsAssignableFrom(configurationType))
                            {
                                var dataElement = Activator.CreateInstance(configurationType);
                                if (dataElement != null)
                                {
                                    DeserializeElementMethodInfo.Invoke(dataElement, new object[] { reader, serializeCollectionKey });
                                    Target = ((IInstancable<T>)dataElement).CreateInstance();
                                    return;
                                }
                            }
                        }
                    }
                    catch
                    {
                    }
                }
            }
            //base.DeserializeElement(reader, serializeCollectionKey);
        }

        protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
        {
            if (writer != null && Type != null && Target != null)
            {
                try
                {
                    var allAttributes = Type.GetCustomAttributes(typeof(ConfigurationElementTypeAttribute), true);
                    if (allAttributes.Length > 0)
                    {
                        var typeAttribute = allAttributes[0] as ConfigurationElementTypeAttribute;
                        var configurationType = typeAttribute.ConfigurationType;
                        if (typeof(TypedConfigurationSection).IsAssignableFrom(configurationType))
                        {
                            var dataElement = Activator.CreateInstance(configurationType, Target) as TypedConfigurationSection;
                            return (bool)SerializeElementMethodInfo.Invoke(dataElement, new object[] { writer, serializeCollectionKey });
                        }
                    }
                }
                catch
                {
                }
                return false;
            }
            return true;
        }
        #endregion

        #region CreateInstance
        public virtual T CreateInstance()
        {
            return Target;
        }
        #endregion
    }
}
